﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;

public class Ethernet
{
    int RECEIVE_SIZE = 1024;

    // ethernet connection variables
    Socket conn;
    IPAddress ipAddr;
    int port;

    // gui element to print out info to the user
    TextBox output; public TextBox getOutput() { return output; }

    // constructor that initiates GUI element and creates connection to specified IP address
    public Ethernet(string ip, int portIn)
    {
        // GUI
        output = new TextBox();
        output.Text = "";
        output.Size = new System.Drawing.Size(350, 400);
        output.Multiline = true;
        output.ScrollBars = ScrollBars.Vertical;
        output.Dock = DockStyle.Bottom;

        // read IP address and create connection
        print("New socket:\n");
        ipAddr = IPAddress.Parse(ip);
        port = portIn;
        IPEndPoint ie = new IPEndPoint(ipAddr, port);

        InitComms();
    }

    // close ethernet socket connection
    public int CloseComms()
    {
        conn.Close();
        return 0;
    }

    // initiate communication
    // keepAlive argument currently not used, so always use InitComms()
    public int InitComms() { return InitComms(true); }

    public int InitComms(bool keepAlive)
    {
        try
        {
            MessageBox.Show(output, "Opening ethernet connection to " + ipAddr.ToString());

            // open socket
            conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            conn.Connect(ipAddr, port);
            conn.Blocking = true;

            print("New socket opened");
            print(conn.ToString());

            if (keepAlive)
            {
                Send("CONNECT Robot_access Keep-Alive:-1");
                Receive(false);
            }
        }
        catch (Exception e)
        {
            // should connection fail, either retry or quit
            DialogResult result = MessageBox.Show("Could not connect to robot\n" + e.Message, "Connection error", MessageBoxButtons.AbortRetryIgnore);
            if (result == DialogResult.Abort)
                Environment.Exit(0);
        }

        return 0; // success
    }

    // send data via ethernet with necessary postfix
    public void Send(String str)
    {
        str = str + "\r\f";
        SendData(str);
    }

    // send clean data via ethernet
    public void SendData(String str)
    {
        try
        {
            byte[] byteArr = Encoding.ASCII.GetBytes(str);
            this.conn.Send(byteArr);
        }
        catch (SocketException s)
        {
            printError(s);
        }
    }

    // send one byte over ethernet
    // not used
    public void SendData(byte b)
    {
        try
        {
            byte[] bytes = new byte[1];
            bytes[0] = b;
            this.conn.Send(bytes);
        }
        catch (SocketException s)
        {
            printError(s);
        }
    }

    // relay function
    public double[] Receive() { return Receive(false); }

    // receive data and return it as a string array
    // this function is used to return ascii messages from the controller
    public string[] ReceiveString()
    {
        byte[] byteArr = new byte[RECEIVE_SIZE];
        try
        {
            conn.Receive(byteArr).ToString();
        }
        catch (Exception e)
        {
            printError(e);
        }

        string[] rec = Encoding.ASCII.GetString(byteArr).Split(',');
        if (rec.Length == 1)
            print(rec[0]);

        return rec;
    }

    // receive data and return it as a double array
    // bool binary controls whether or not data is printed as binary first
    // should error occur with conversion to double -1 is returned instead
    public double[] Receive(bool binary)
    {
        byte[] byteArr = new byte[RECEIVE_SIZE];
        double[] doubleArr;

        try
        {
            conn.Receive(byteArr).ToString();

            string[] rec = Encoding.ASCII.GetString(byteArr).Split(',');
            int[] recInt = new int[rec.Length];
            doubleArr = new double[rec.Length];

            if (binary)
            {
                String bins = "";
                for (int a = 0; a < rec.Length; a++)
                {
                    recInt[a] = int.Parse(rec[a]);
                    bins = bins + " " + Convert.ToString(recInt[a], 2).PadLeft(8, '0');
                    doubleArr[a] = double.Parse(rec[a]);
                }

                print("Received: " + bins + "\n");
            }
            else
            {
                if (rec[0].Contains("(2070)."))
                    MessageBox.Show("Servo is OFF", "Error");
                else
                    for (int a = 0; a < rec.Length; a++)
                    {
                        try
                        {
                            doubleArr[a] = double.Parse(rec[a]);
                        }
                        catch (FormatException) { doubleArr[a] = -1; }
                    }
            }

            return doubleArr;
        }
        catch (Exception e)
        {
            printError(e);
            CloseComms();
            InitComms();
            return new double[0];
        }
    }

    // print an error message to output
    public void printError(Exception s)
    {
        print("Error: '" + s.Message + "\n" + s.StackTrace);
    }

    // print input string to the output screen
    // used mostly for debug messages
    public void print(String newOutput)
    {
        this.output.AppendText(newOutput + "\n\n");
    }
}